package com.yuandengta.yomall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.yuandengta.common.utils.JsonUtils;
import com.yuandengta.yomall.product.dao.CategoryBrandRelationDao;
import com.yuandengta.yomall.product.entity.CategoryBrandRelationEntity;
import com.yuandengta.yomall.product.vo.Catalog2VO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuandengta.common.utils.PageUtils;
import com.yuandengta.common.utils.Query;

import com.yuandengta.yomall.product.dao.CategoryDao;
import com.yuandengta.yomall.product.entity.CategoryEntity;
import com.yuandengta.yomall.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**
 * 分类管理业务实现类
 */
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private CategoryBrandRelationDao categoryBrandRelationDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 查询所有分类及子分类，以树形结构组装
     * @return 所有分类（树形结构）
     */
    @Override
    public List<CategoryEntity> listWithTree() {
        // 查询所有分类
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        // 组装分类父子树形结构
        List<CategoryEntity> level1Category = categoryEntities.stream()
                // 查询一级分类
                .filter(categoryEntity -> categoryEntity.getParentCid() == 0)
                // 递归查找当前一级分类所有子分类
                .map(categoryEntity -> {
                    categoryEntity.setChildren(getChildren(categoryEntity, categoryEntities));
                    return categoryEntity;
                })
                // 排序
                .sorted((categoryEntity1, categoryEntity2) -> {
                    return (categoryEntity1.getSort() == null ? 0 : categoryEntity1.getSort()) -
                            (categoryEntity2.getSort() == null ? 0 : categoryEntity2.getSort());
                })
                .collect(Collectors.toList());

        return level1Category;
    }

    /**
     * 递归查找所有分类的子分类
     * @param root 当前一级分类
     * @param all 所有分类
     * @return
     */
    private List<CategoryEntity> getChildren (CategoryEntity root, List<CategoryEntity> all) {
        List<CategoryEntity> children = all.stream()
                .filter(categoryEntity -> categoryEntity.getParentCid().equals(root.getCatId()))
                .map(categoryEntity -> {
                    // 递归找子分类 getChildren
                    categoryEntity.setChildren(getChildren(categoryEntity, all));
                    return categoryEntity;
                })
                .sorted((categoryEntity1, categoryEntity2) -> {
                    // 排序
                    return (categoryEntity1.getSort() == null ? 0 : categoryEntity1.getSort()) -
                            (categoryEntity2.getSort() == null ? 0 : categoryEntity2.getSort());
                })
                .collect(Collectors.toList());

        return children;
    }

    /**
     * 查找三级分类的完整路径
     * @param catelogId 三级分类id
     * @return 三级分类的完整路径
     */
    @Override
    public Long[] findCategoryPath(Long catelogId) {
        List<Long> paths = new ArrayList<>();
        List<Long> fullPath = findParentPath(catelogId, paths);
        // 集合数据进行逆序
        Collections.reverse(fullPath);
        return fullPath.toArray(new Long[fullPath.size()]);
    }

    /**
     * 递归收集三级分类的父id
     * @param catelogId
     * @param paths
     * @return
     */
    private List<Long> findParentPath(Long catelogId, List<Long> paths) {
        // 收集当前节点id
        paths.add(catelogId);

        // 查询当前分类的信息
        CategoryEntity categoryEntity = getById(catelogId);
        if (categoryEntity.getParentCid() != 0) {
            // 递归
            findParentPath(categoryEntity.getParentCid(), paths);
        }

        return paths;
    }

    /**
     * 级联更新分类
     * @param category
     */
    @CacheEvict(value = {"category"}, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateCascade(CategoryEntity category) {
        // 更新自己的表数据
        this.updateById(category);

        if (!StringUtils.isEmpty(category.getName())) {
            // 同步更新品牌分类关联表中的数据
            CategoryBrandRelationEntity categoryBrandRelationEntity = new CategoryBrandRelationEntity();
            categoryBrandRelationEntity.setCatelogId(category.getCatId());
            categoryBrandRelationEntity.setCatelogName(category.getName());
            categoryBrandRelationDao.update(categoryBrandRelationEntity,
                    new UpdateWrapper<CategoryBrandRelationEntity>()
                            .eq("catelog_id", category.getCatId()));
        }
    }

    /**
     * 查询一级分类
     * @return 一级分类集合
     */
    @Cacheable(value = {"category"}, key = "#root.methodName")
    @Override
    public List<CategoryEntity> getLevel1Categories() {
        System.out.println("getLevel1Categories()...");
        List<CategoryEntity> entities = list(
                new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return entities;
    }

    /**
     * 查询首页展示分类列表
     * @return
     */
    @Cacheable(value = {"category"}, key = "#root.methodName")
    @Override
    public Map<String, List<Catalog2VO>> getCatalogJson() {
        // 查询出所有的分类数据
        List<CategoryEntity> entities = list(null);

        // 查询所有一级分类
        List<CategoryEntity> level1Categories = getParents(entities, 0L);

        // 封装数据
        Map<String, List<Catalog2VO>> map = level1Categories.stream().collect(
                Collectors.toMap(k -> k.getCatId().toString(), v -> {

                    // 查询当前一级分类的所有二级分类，封装成vo
                    List<CategoryEntity> level2Categories = getParents(entities, v.getCatId());

                    List<Catalog2VO> catalog2VOS = null;
                    if (level2Categories != null) {
                        catalog2VOS = level2Categories.stream().map(category2 -> {
                            Catalog2VO catalog2VO = new Catalog2VO(
                                    v.getCatId().toString(),
                                    null,
                                    category2.getCatId().toString(),
                                    category2.getName());

                            // 查询当前二级分类的所有三级分类，封装为vo
                            List<CategoryEntity> level3Categories = getParents(entities, category2.getCatId());
                            if (level3Categories != null) {
                                List<Catalog2VO.Catalog3VO> catalog3VOS = level3Categories.stream().map(catalog3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(
                                            category2.getCatId().toString(),
                                            catalog3.getCatId().toString(),
                                            catalog3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                catalog2VO.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }
                    return catalog2VOS;
                }));

        return map;
    }

    /**
     * 查询首页展示分类列表
     *
     * 使用缓存
     * 1. 解决缓存穿透：空结果缓存
     * 2. 解决缓存雪崩：设置过期期间
     * 3. 解决缓存击穿：加锁
     * 4. 缓存数据一致性
     *  1）双写模式
     *  2）失效模式
     *
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatalogJson2() {
        // 优先从缓存中获取数据
        String catalogJson = redisTemplate.opsForValue().get("catalogJson");

        if (StringUtils.isEmpty(catalogJson)) {
            // 缓存中没有，则从数据库查询
            Map<String, List<Catalog2VO>> catalogJsonFromDb = getCatalogJsonFromDbWithLock();
            return catalogJsonFromDb;
        }

        // 将json转换为 Map对象
        Map<String, List<Catalog2VO>> stringListMap = JsonUtils.jsonToMapList(catalogJson, Catalog2VO.class);

        return stringListMap;
    }

    /**
     * 加锁
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatalogJsonFromDbWithLock() {
        // 锁的粒度越细越快
        RLock lock = redissonClient.getLock("catalogJsonLock");
        lock.lock();

        Map<String, List<Catalog2VO>> catalogJsonFromDb = null;
        try {
            catalogJsonFromDb = getCatalogJsonFromDb();
        } finally {
            lock.unlock();
        }

        return catalogJsonFromDb;
    }

    /**
     * 从数据库查询分类
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatalogJsonFromDb() {
        // 从缓存中查询数据
        String catalogJson = redisTemplate.opsForValue().get("catalogJson");
        if (!StringUtils.isEmpty(catalogJson)) {
            Map<String, List<Catalog2VO>> stringListMap = JsonUtils.jsonToMapList(catalogJson, Catalog2VO.class);
            return stringListMap;
        }

        // 查询出所有的分类数据
        List<CategoryEntity> entities = list(null);

        // 查询所有一级分类
        List<CategoryEntity> level1Categories = getParents(entities, 0L);

        // 封装数据
        Map<String, List<Catalog2VO>> map = level1Categories.stream().collect(
                Collectors.toMap(k -> k.getCatId().toString(), v -> {

                    // 查询当前一级分类的所有二级分类，封装成vo
                    List<CategoryEntity> level2Categories = getParents(entities, v.getCatId());

                    List<Catalog2VO> catalog2VOS = null;
                    if (level2Categories != null) {
                        catalog2VOS = level2Categories.stream().map(category2 -> {
                            Catalog2VO catalog2VO = new Catalog2VO(
                                    v.getCatId().toString(),
                                    null,
                                    category2.getCatId().toString(),
                                    category2.getName());

                            // 查询当前二级分类的所有三级分类，封装为vo
                            List<CategoryEntity> level3Categories = getParents(entities, category2.getCatId());
                            if (level3Categories != null) {
                                List<Catalog2VO.Catalog3VO> catalog3VOS = level3Categories.stream().map(catalog3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(
                                            category2.getCatId().toString(),
                                            catalog3.getCatId().toString(),
                                            catalog3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                catalog2VO.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }
                    return catalog2VOS;
                }));

        // 转换为 json并放入到缓存中
        String json = JsonUtils.objectToJson(map);
        redisTemplate.opsForValue().set("catalogJson", json, 1, TimeUnit.DAYS);

        return map;
    }

    /**
     * 根据分类父id查找所有的子分类
     * @param categories 所有分类数据
     * @param parentId 分类父id
     * @return
     */
    private List<CategoryEntity> getParents(List<CategoryEntity> categories, Long parentId) {
        List<CategoryEntity> entities = categories.stream()
                .filter(categoryEntity -> categoryEntity.getParentCid().equals(parentId))
                .collect(Collectors.toList());
        return entities;
    }

}